﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.WebPages;
using Bootstrap.MVC.Utils;
using Bootstrap.MVC.Widgets;

namespace Bootstrap.MVC.Infrastructure.Html
{
    public class HtmlTagBuilder
    { 
        protected string InnerHtml { set; get; }
        protected  string InnerText { set; get; }


        #region Public Propertis
        public TagRenderMode RenderMode { set; get; }
        public Func<dynamic, HelperResult> Template;
        #endregion Public Properties

        
        #region Private Propertis
        private IList<HtmlTagBuilder> children;
        private string tagName;
        private Dictionary<string, object> attributes;
        private Dictionary<string, object> extraAttributes;
        #endregion Private Properties


        #region Constructors and protected
        public HtmlTagBuilder(string tagName) : this(tagName, null) {}

        public HtmlTagBuilder(string tagName, Dictionary<string,object> extraAttributes)
        {
            Guard.IsNotNullOrEmpty(tagName, "tagName");

            children = new List<HtmlTagBuilder>();
            RenderMode = TagRenderMode.Normal;
            this.tagName = tagName;
            attributes = new Dictionary<string, object>();
            if(extraAttributes != null)
                this.extraAttributes = extraAttributes;
        }

        protected string RenderTemplate()
        {
            return Template == null ?
                string.Empty :
                new HelperResult(writer => Template(1).WriteTo(writer)).ToString();
        }
        #endregion


        #region Public Methods
        public HtmlTagBuilder Append(HtmlTagBuilder child)
        {
            Guard.IsNotNull(child, "child");

            children.Add(child);

            return this;
        }

        public HtmlTagBuilder Attr(object attribute, bool replaceExisting = true)
        {
            Guard.IsNotNull(attribute, "attribute");

            var attDic= new RouteValueDictionary(attribute);

            foreach (var att in attDic)
                Attr(att.Key, att.Value.ToString(), replaceExisting);

            return this;
        }

        public HtmlTagBuilder Attr(string key, object value, bool replaceExisting = true)
        {
            Guard.IsNotNullOrEmpty(key, "key");
            Guard.IsNotNull(value, "value");

            key=key.ToLower().Trim();
            if (attributes.ContainsKey(key))
            {
                if (replaceExisting)
                {
                    attributes[key] = value;
                }
                else
                {
                    attributes[key] = (key == "style") ?
                                attributes[key].ToString().AppendWithSemicolon(value.ToString()) :
                                attributes[key].ToString().AppendWithSpace(value.ToString());
                }
            }
            else
            {
                attributes.Add(key, value);
            }

            return this;
        }

        public HtmlTagBuilder Text(string text)
        {
            InnerText = text;
            return this;
        }

        public HtmlTagBuilder Html(string html)
        {
            InnerHtml = html;
            return this;
        }

        public HtmlTagBuilder AddClass(string cssClass)
        {
            Guard.IsNotNullOrEmpty(cssClass, "cssClass");

            Attr("class", cssClass, false);

            return this;
        }

        public string Render(bool innerTextFirst= true)
        {
            // merge extraAttributes with attributes
            mergeAttributes();   

            var tagBuilder = new TagBuilder(tagName);
            
            tagBuilder.MergeAttributes(attributes);
            
            if (RenderMode == TagRenderMode.SelfClosing)
                return tagBuilder.ToString();

            if (Template != null)
            {
                tagBuilder.InnerHtml = RenderTemplate();
                return tagBuilder.ToString();
            }

            var sb = new StringBuilder();

            sb.Append(tagBuilder.ToString(TagRenderMode.StartTag));

           

            if (children != null && children.Count > 0)
            { 
                if (innerTextFirst && !string.IsNullOrEmpty(InnerText)) sb.Append(InnerText);
                
                foreach (var child in children)
                {
                    var childBuilder = new TagBuilder(child.tagName);
                    sb.Append(child.Render());
                }

                if (!innerTextFirst && !string.IsNullOrEmpty(InnerText)) sb.Append(InnerText);
            }
            else
            {
                sb.Append(string.IsNullOrEmpty(InnerHtml) ? InnerText : InnerHtml);
            }
            sb.Append(tagBuilder.ToString(TagRenderMode.EndTag));
            return sb.ToString();

        }

        private void mergeAttributes()
        {
            if(extraAttributes != null){
                foreach(var attr in extraAttributes)
                {
                    if(attr.Key.ToLower() =="class")
                        AddClass(attr.Value.ToString());
                    else
                        Attr(attr.Key,attr.Value,true);
                }
            }
        }
        #endregion Public Methods
    }
}
